[No QA] Create Merchant rules page#80536
Conversation
This comment has been minimized.
This comment has been minimized.
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index e0ec4822..6d54352c 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -640,6 +640,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: 'Ursprünglicher Betrag',
insights: 'Einblicke',
duplicateExpense: 'Doppelte Ausgabe',
+ newFeature: 'Neue Funktion',
},
supportalNoAccess: {
title: 'Nicht so schnell',
@@ -6316,6 +6317,16 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
title: 'Spesenrichtlinie',
cardSubtitle: 'Hier befindet sich die Spesenrichtlinie eures Teams, damit alle genau wissen, was abgedeckt ist.',
},
+ merchantRules: {
+ title: 'Händler',
+ subtitle: 'Legen Sie Händlerregeln fest, damit Ausgaben korrekt codiert ankommen und weniger Nachbearbeitung erfordern.',
+ addRule: 'Händlerregel hinzufügen',
+ ruleSummaryTitle: (merchantName: string) => `Wenn Händler „${merchantName}“ enthält`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `Händler in „${merchantName}“ umbenennen`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `Aktualisiere ${fieldName} zu „${fieldValue}“`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `Als "${reimbursable ? 'erstattungsfähig' : 'nicht erstattungsfähig'}" markieren`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `Als „${billable ? 'Abrechenbar' : 'nicht abrechenbar'}“ markieren`,
+ },
},
planTypePage: {
planTypes: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 8cbc089f..7a781bc2 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -642,6 +642,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: 'Montant d’origine',
insights: 'Analyses',
duplicateExpense: 'Note de frais en double',
+ newFeature: 'Nouvelle fonctionnalité',
},
supportalNoAccess: {
title: 'Pas si vite',
@@ -6327,6 +6328,16 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin
title: 'Politique de dépenses',
cardSubtitle: 'Voici l’endroit où se trouve la politique de dépenses de votre équipe, afin que tout le monde soit sur la même longueur d’onde concernant ce qui est couvert.',
},
+ merchantRules: {
+ title: 'Commerçant',
+ subtitle: 'Définissez les règles de marchand afin que les dépenses arrivent correctement codées et nécessitent moins de nettoyage.',
+ addRule: 'Ajouter une règle de commerçant',
+ ruleSummaryTitle: (merchantName: string) => `Si le commerçant contient « ${merchantName} »`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `Renommer le marchand en « ${merchantName} »`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `Mettre à jour ${fieldName} sur « ${fieldValue} »`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `Marquer comme « ${reimbursable ? 'remboursable' : 'non remboursable'} »`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `Marquer comme « ${billable ? 'facturable' : 'non facturable'} »`,
+ },
},
planTypePage: {
planTypes: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index deb48891..97ce434b 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -641,6 +641,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: 'Importo originale',
insights: 'Analisi',
duplicateExpense: 'Spesa duplicata',
+ newFeature: 'Nuova funzionalità',
},
supportalNoAccess: {
title: 'Non così in fretta',
@@ -6300,6 +6301,16 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori
title: 'Policy di spesa',
cardSubtitle: 'Qui è dove si trova la policy di spesa del tuo team, così tutti sono allineati su cosa è coperto.',
},
+ merchantRules: {
+ title: 'Esercente',
+ subtitle: 'Imposta le regole per gli esercenti in modo che le spese arrivino già codificate correttamente e richiedano meno correzioni.',
+ addRule: 'Aggiungi regola esercente',
+ ruleSummaryTitle: (merchantName: string) => `Se l’esercente contiene "${merchantName}"`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `Rinomina esercente in "${merchantName}"`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `Aggiorna ${fieldName} a "${fieldValue}"`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `Segna come "${reimbursable ? 'rimborsabile' : 'non rimborsabile'}"`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `Contrassegna come "${billable ? 'fatturabile' : 'non fatturabile'}"`,
+ },
},
planTypePage: {
planTypes: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index d3cfcd26..0a70a593 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -640,6 +640,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: '元の金額',
insights: 'インサイト',
duplicateExpense: '重複した経費',
+ newFeature: '新機能',
},
supportalNoAccess: {
title: 'ちょっと待ってください',
@@ -6257,6 +6258,16 @@ ${reportName}
title: '経費ポリシー',
cardSubtitle: 'ここにはチームの経費ポリシーが保存されています。これにより、何が対象になるかについて全員が同じ認識を持てます。',
},
+ merchantRules: {
+ title: '加盟店',
+ subtitle: '取引先ルールを設定して、経費が正しくコード化された状態で届くようにし、後処理を最小限に抑えましょう。',
+ addRule: '店舗ルールを追加',
+ ruleSummaryTitle: (merchantName: string) => `もし取引先に「${merchantName}」が含まれている場合`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `支払先名を「${merchantName}」に変更`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `${fieldName} を「${fieldValue}」に更新`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `「${reimbursable ? '払い戻し対象' : '精算対象外'}」としてマーク`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `「${billable ? '請求可能' : '請求対象外'}」としてマーク`,
+ },
},
planTypePage: {
planTypes: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 90027e14..a4421c1d 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -641,6 +641,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: 'Oorspronkelijk bedrag',
insights: 'Inzichten',
duplicateExpense: 'Dubbele uitgave',
+ newFeature: 'Nieuwe functie',
},
supportalNoAccess: {
title: 'Niet zo snel',
@@ -6286,6 +6287,16 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten
title: 'Onkostennota-beleid',
cardSubtitle: 'Hier staat het onkostebeleid van je team, zodat iedereen goed weet wat wel en niet wordt vergoed.',
},
+ merchantRules: {
+ title: 'Handelaar',
+ subtitle: 'Stel de merchantregels zo in dat onkosten met de juiste codering binnenkomen en er minder nabewerking nodig is.',
+ addRule: 'Merchantregel toevoegen',
+ ruleSummaryTitle: (merchantName: string) => `Als handelaar "${merchantName}" bevat`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `Naam handelaar wijzigen in "${merchantName}"`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `Werk ${fieldName} bij naar "${fieldValue}"`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `Markeren als "${reimbursable ? 'Vergoedbaar' : 'niet-vergoedbaar'}"`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `Markeren als "${billable ? 'factureerbaar' : 'niet-factureerbaar'}"`,
+ },
},
planTypePage: {
planTypes: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index b39f9c43..28a17b01 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -641,6 +641,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: 'Kwota pierwotna',
insights: 'Analizy',
duplicateExpense: 'Zduplikowany wydatek',
+ newFeature: 'Nowa funkcja',
},
supportalNoAccess: {
title: 'Nie tak szybko',
@@ -6280,6 +6281,16 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
title: 'Polityka wydatków',
cardSubtitle: 'Tutaj znajduje się polityka wydatków Twojego zespołu, aby wszyscy mieli jasność, co jest objęte.',
},
+ merchantRules: {
+ title: 'Sprzedawca',
+ subtitle: 'Skonfiguruj reguły dla sprzedawców, aby wydatki trafiały z poprawnym kodowaniem i wymagały mniej poprawek.',
+ addRule: 'Dodaj regułę sprzedawcy',
+ ruleSummaryTitle: (merchantName: string) => `Jeśli sprzedawca zawiera „${merchantName}”`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `Zmień sprzedawcę na „${merchantName}”`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `Zaktualizuj ${fieldName} na „${fieldValue}”`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `Oznacz jako "${reimbursable ? 'kwalifikujący się do zwrotu kosztów' : 'niepodlegający zwrotowi'}"`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `Oznacz jako „${billable ? 'fakturowalne' : 'poza fakturą'}”`,
+ },
},
planTypePage: {
planTypes: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 334edd7e..ef12d476 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -640,6 +640,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: 'Valor original',
insights: 'Insights',
duplicateExpense: 'Despesa duplicada',
+ newFeature: 'Novo recurso',
},
supportalNoAccess: {
title: 'Não tão rápido',
@@ -6281,6 +6282,16 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe
title: 'Política de despesas',
cardSubtitle: 'É aqui que fica a política de despesas da sua equipe, para que todos estejam alinhados sobre o que é coberto.',
},
+ merchantRules: {
+ title: 'Estabelecimento',
+ subtitle: 'Defina as regras de comerciante para que as despesas cheguem corretamente categorizadas e exijam menos retrabalho.',
+ addRule: 'Adicionar regra de estabelecimento',
+ ruleSummaryTitle: (merchantName: string) => `Se o comerciante contiver "${merchantName}"`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `Renomear comerciante para "${merchantName}"`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `Atualizar ${fieldName} para "${fieldValue}"`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `Marcar como "${reimbursable ? 'reembolsável' : 'não reembolsável'}"`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `Marcar como "${billable ? 'faturável' : 'não faturável'}"`,
+ },
},
planTypePage: {
planTypes: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 4ea00ed4..5c997de1 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -637,6 +637,7 @@ const translations: TranslationDeepObject<typeof en> = {
originalAmount: '原始金额',
insights: '洞察',
duplicateExpense: '重复报销',
+ newFeature: '新功能',
},
supportalNoAccess: {
title: '先别急',
@@ -6147,6 +6148,16 @@ ${reportName}
title: '报销政策',
cardSubtitle: '这里是你们团队报销政策所在的位置,让所有人都能清楚了解哪些费用包含在内。',
},
+ merchantRules: {
+ title: '商家',
+ subtitle: '设置商家规则,让报销费用自动按正确科目归类,减少后期清理工作。',
+ addRule: '添加商家规则',
+ ruleSummaryTitle: (merchantName: string) => `如果商户包含“${merchantName}”`,
+ ruleSummarySubtitleMerchant: (merchantName: string) => `将商家重命名为 “${merchantName}”`,
+ ruleSummarySubtitleUpdateField: (fieldName: string, fieldValue: string) => `将 ${fieldName} 更新为“${fieldValue}”`,
+ ruleSummarySubtitleReimbursable: (reimbursable: boolean) => `标记为“${reimbursable ? '可报销' : '不予报销'}”`,
+ ruleSummarySubtitleBillable: (billable: boolean) => `标记为“${billable ? '可计费' : '不可计费'}”`,
+ },
},
planTypePage: {
planTypes: {
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
- Use getDecodedCategoryName to properly decode HTML entities in category names - Use getCleanedTagName to handle escaped characters in tag names - Matches the pattern used in ExpenseRuleUtils
ISO 8601 timestamps are lexicographically sortable, so string comparison works correctly
|
Updated! @cead22 |
Reviewer Checklist
|
Move the explanation to the JSDoc comment instead of inline with the rule name
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
Good catch! Th top one doesn't look 15px does it |
|
Thanks! I'm addressing that in my next PR #80545 |
|
Thank you! |
|
🚀 Deployed to staging by https://github.com/luacmartins in version: 9.3.10-0 🚀
|
|
🚀 Deployed to production by https://github.com/roryabraham in version: 9.3.10-6 🚀
|
| left: string; | ||
|
|
||
| /** The operator for the filter, defined in CONST.SEARCH.SYNTAX_OPERATORS */ | ||
| operator: string; |
There was a problem hiding this comment.
This should have been typed better
| operator: string; | |
| operator: ValueOf<typeof CONST.SEARCH.SYNTAX_OPERATORS>; |


Explanation of Change
Adds a merchant rule section to the workspace rules page.
Fixed Issues
$ #80519
Tests
Workspace > More featuresRulesRulespageMerchantsection and that it looks like the UI below.NOTE: This section is only available in dev for now.
Offline tests
N/A
QA Steps
N/A
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari