Skip to content

[Rules Engine Release 2] [App] Support expense defaults for more than just merchants - #104155

Open
Krishna2323 wants to merge 20 commits into
Expensify:mainfrom
Krishna2323:krishna2323/issue-102277-expense-defaults
Open

Krishna2323 wants to merge 20 commits into
Expensify:mainfrom
Krishna2323:krishna2323/issue-102277-expense-defaults

Conversation

@Krishna2323

Copy link
Copy Markdown
Contributor

Explanation of Change

An expense default rule could only match on a single merchant. The filter tree is now built by ANDing every condition the form sets, so a rule can match on a merchant, a list of categories and a list of tags at once, and it is read back by walking the tree rather than reading one comparison.

  • buildMerchantRuleFilters builds a left-nested and tree, so three conditions become and(and(merchant, category), tag). Each multi-select stays one comparison with an array on the right, which the backend matches as an OR.
  • getEditableConditions and getConditionFromLeaf replace getEditableMerchantMatch. A rule stays read-only when the form cannot represent its tree: an or combination, the same field matched twice, a field the form has no input for, or an operator it has no control for.
  • isExpenseDefaultRule required a merchant condition, which would have hidden every rule matching on a category or a tag alone. It now requires at least one condition.
  • Duplicate detection compares the whole condition set instead of the merchant and match type, ignoring the order of a multi-select and only comparing the match type when both rules match on a merchant.

This is the first step of #102277. The category condition is not wired to the new save path yet: isCategoryRule is still true whenever categoriesToMatch is set, and that branch returns before setRule, so a category default still writes to policy.rules.expenseRules. A follow-up removes that branch.

Fixed Issues

$ #102277
PROPOSAL:

Tests

  1. Open a workspace with the rules engine beta enabled, go to Workspace settings > Rules > Expense defaults.
  2. Click Add default, pick Merchant, enter a merchant to match, set a category and a tag, save.
  3. Verify the rule is listed with the merchant as its condition and the category and tag as its defaults.
  4. Open the rule again and verify every field is populated with what you saved.
  5. Change the merchant and save. Verify the row updates.
  6. Create a second rule with the same merchant and the same match type. Verify the "Similar merchant rule already exists" prompt appears.
  7. Dismiss it, change the match type from Contains to Exact match, and save. Verify the prompt does not appear, because the two rules no longer match the same expenses.
  8. Create an expense whose merchant matches the rule. Verify the category and tag from the rule are applied.
  9. Delete the rules you created and verify they disappear from the table.
  10. Verify that no errors appear in the JS console

Offline tests

  1. Go offline.
  2. Create a merchant rule with a category and a tag. Verify it appears in the table greyed out as pending.
  3. Edit it while still offline. Verify the row shows the edited values, still pending.
  4. Go back online. Verify the rule saves, loses the pending style, and shows the same values it showed offline.

QA Steps

  1. Open a workspace with the rules engine beta enabled, go to Workspace settings > Rules > Expense defaults.
  2. Click Add default, pick Merchant, enter a merchant to match, set a category and a tag, save.
  3. Verify the rule is listed with the merchant as its condition and the category and tag as its defaults.
  4. Open the rule again and verify every field is populated with what you saved.
  5. Create a second rule with the same merchant and the same match type. Verify the "Similar merchant rule already exists" prompt appears.
  6. Dismiss it, switch the match type from Contains to Exact match, save, and verify the prompt does not appear.
  7. Create an expense whose merchant matches the rule. Verify the category and tag are applied to it.
  8. Go to Workspace settings > Categories, open a category, set a default tax rate on it, and verify it still saves and still appears in the Expense defaults table.
  9. Delete the rules you created and verify they disappear from the table.
  10. Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
    • MacOS: Desktop
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified there are no new alerts related to the canBeMissing param for useOnyx
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/CONST/index.ts 95.90% <ø> (ø)
src/SCREENS.ts 100.00% <ø> (ø)
src/libs/API/types.ts 100.00% <ø> (ø)
src/libs/CategoryContextualRulesUtils.ts 94.11% <100.00%> (ø)
src/libs/CategoryTaxRulesUtils.ts 94.11% <100.00%> (+7.23%) ⬆️
src/libs/CategoryUtils.ts 90.16% <ø> (+8.07%) ⬆️
src/libs/MerchantRuleSuggestionUtils.ts 88.88% <100.00%> (ø)
...gation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts 100.00% <ø> (ø)
src/libs/Navigation/linkingConfig/config.ts 62.50% <ø> (ø)
src/libs/ReportUtils.ts 88.69% <ø> (+0.01%) ⬆️
... and 28 more
... and 13 files with indirect coverage changes

@melvin-bot

melvin-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running:

npx bun ./scripts/generateTranslations.ts --help

Typically, you'd want to translate only what you changed by running npx bun ./scripts/generateTranslations.ts --compare-ref main

@JS00001

JS00001 commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Ran an initial test of this locally, this works great!! Nice work on this

@Krishna2323 Krishna2323 changed the title Krishna2323/issue 102277 expense defaults [Rules Engine Release 2] [App] Support expense defaults for more than just merchants Oct 19, 2026
…02277-expense-defaults

# Conflicts:
#	src/libs/Navigation/types.ts
@Krishna2323

Copy link
Copy Markdown
Contributor Author

@JS00001 please run translations script when you get a chance.

@OSBotify

Copy link
Copy Markdown
Contributor

🦜 Polyglot Parrot! 🦜

Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues:

View the translation diff
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 8fe7783c..73bc0d25 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -8526,18 +8526,8 @@ Fordern Sie Spesendetails wie Belege und Beschreibungen an, legen Sie Limits und
                 vendorUnavailable: 'Anbieter nicht verfügbar',
                 supplierUnavailable: 'Lieferant nicht verfügbar',
                 confirmErrorCategory: 'Bitte wählen Sie eine Kategorie aus',
-                confirmErrorCategoryTax: 'Bitte wählen Sie einen Steuersatz aus',
                 confirmErrorCondition: 'Bitte geben Sie einen Händler ein',
                 confirmErrorConditionAndDefault: 'Geben Sie einen Händler ein und wenden Sie mindestens eine Standardeinstellung an',
-                turnOnTaxesFirstTitle: 'Schalten Sie zuerst Steuern ein',
-                turnOnTaxesFirstPrompt: 'Kategorienregeln legen einen Standardsteuersatz fest. Aktivieren Sie Steuern in Ihren Workspace-Einstellungen, um sie zu verwenden.',
-                categoryRulesApplyGoingForwardTitle: 'Kategorienregeln gelten ab jetzt',
-                categoryRulesApplyGoingForwardPrompt: 'Ein Standardsatz für Steuern gilt für neue Ausgaben in dieser Kategorie. Bereits vorhandene Ausgaben werden nicht geändert.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'Der ausgewählte Steuersatz ist jetzt die Voreinstellung für Ihren Workspace, daher ist diese Regel nicht mehr gültig. Wählen Sie einen anderen Steuersatz.',
-                addTaxRateFirstTitle: 'Fügen Sie zuerst einen Steuersatz hinzu',
-                addTaxRateFirstPrompt:
-                    'Kategorienregeln legen einen standardmäßigen Steuersatz fest. Fügen Sie einen anderen Steuersatz als den Standard Ihres Arbeitsbereichs hinzu, um sie zu verwenden.',
                 createRuleFromExpenseAction: 'Regel erstellen',
                 createRuleFromExpensePrompt: 'um Ihre Änderungen auf alle Ausgaben anzuwenden, die Ihren Kriterien entsprechen.',
             },
@@ -8854,6 +8844,7 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
                 merchantIs: (merchant: string) => `Händler ist „${merchant}“`,
                 merchantTypeIs: (merchantType: string) => `Händlertyp ist „${merchantType}"`,
                 categoryIs: (category: string) => `Kategorie ist „${category}“`,
+                tagIs: (tag: string) => `Tag ist „${tag}“`,
             },
             merchantTypeRule: {merchantType: 'Händlerart', saveRule: 'Regel speichern', confirmErrorCategory: 'Bitte wählen Sie eine Kategorie aus.'},
             requireFieldsTable: {
@@ -8923,14 +8914,6 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
                 cta: 'KI-Regel hinzufügen',
             },
             categoriesDisabledEmptyState: {title: 'Kategorien sind nicht aktiviert', subtitle: 'Aktivieren Sie Kategorien, um Ihre Ausgaben besser zu kontrollieren.'},
-            expenseDefaultType: {
-                title: 'Standardspesen übernehmen',
-                subtitle: 'Worauf soll diese Regel angewendet werden?',
-                merchant: 'Händler',
-                merchantDescription: 'Felder bei Ausgaben eines bestimmten Händlers aktualisieren',
-                category: 'Kategorie',
-                categoryDescription: 'Legen Sie einen Standardsteuersatz für eine bestimmte Kategorie fest',
-            },
             taxesDisabledEmptyState: {
                 title: 'Steuern sind nicht aktiviert',
                 subtitle: 'Aktivieren Sie Steuern, um anfallende Steuern zu dokumentieren und zurückzufordern.',
diff --git a/src/languages/el.ts b/src/languages/el.ts
index aa8734fa..13497b18 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -8899,20 +8899,8 @@ ${reportName}`,
                 vendorUnavailable: 'Ο προμηθευτής δεν είναι διαθέσιμος',
                 supplierUnavailable: 'Ο προμηθευτής δεν είναι διαθέσιμος',
                 confirmErrorCategory: 'Παρακαλώ επιλέξτε κατηγορία',
-                confirmErrorCategoryTax: 'Επιλέξτε έναν φορολογικό συντελεστή',
                 confirmErrorCondition: 'Παρακαλούμε εισαγάγετε έναν έμπορο',
                 confirmErrorConditionAndDefault: 'Προσθέστε έναν έμπορο και ορίστε τουλάχιστον μία προεπιλογή',
-                turnOnTaxesFirstTitle: 'Ενεργοποιήστε πρώτα τους φόρους',
-                turnOnTaxesFirstPrompt:
-                    'Οι κανόνες κατηγορίας ορίζουν έναν προεπιλεγμένο φορολογικό συντελεστή. Ενεργοποιήστε τους φόρους στις ρυθμίσεις του χώρου εργασίας σας για να τους χρησιμοποιήσετε.',
-                categoryRulesApplyGoingForwardTitle: 'Οι κανόνες κατηγορίας ισχύουν από εδώ και στο εξής',
-                categoryRulesApplyGoingForwardPrompt:
-                    'Ένας προεπιλεγμένος φορολογικός συντελεστής εφαρμόζεται σε νέες δαπάνες σε αυτή την κατηγορία. Οι δαπάνες που υπάρχουν ήδη δεν θα αλλάξουν.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'Ο επιλεγμένος φορολογικός συντελεστής είναι πλέον η προεπιλογή για τον χώρο εργασίας σας, επομένως αυτός ο κανόνας δεν είναι πλέον έγκυρος. Επιλέξτε έναν διαφορετικό φορολογικό συντελεστή.',
-                addTaxRateFirstTitle: 'Προσθέστε πρώτα έναν φορολογικό συντελεστή',
-                addTaxRateFirstPrompt:
-                    'Οι κανόνες κατηγορίας ορίζουν έναν προεπιλεγμένο φορολογικό συντελεστή. Προσθέστε έναν φορολογικό συντελεστή διαφορετικό από την προεπιλογή του χώρου εργασίας σας για να τους χρησιμοποιήσετε.',
                 createRuleFromExpenseAction: 'Δημιουργία κανόνα',
                 createRuleFromExpensePrompt: 'για να εφαρμόσετε τις αλλαγές σας σε όλες τις δαπάνες που ταιριάζουν με τα κριτήριά σας.',
             },
@@ -8948,6 +8936,7 @@ ${reportName}`,
                 merchantIs: (merchant: string) => `Ο έμπορος είναι «${merchant}»`,
                 merchantTypeIs: (merchantType: string) => `Ο τύπος εμπόρου είναι «${merchantType}»`,
                 categoryIs: (category: string) => `Η κατηγορία είναι «${category}»`,
+                tagIs: (tag: string) => `Η ετικέτα είναι «${tag}»`,
             },
             merchantTypeRule: {
                 merchantType: 'Τύπος εμπόρου',
@@ -9243,14 +9232,6 @@ ${reportName}`,
                 subtitle: 'Δημιουργήστε έναν κανόνα για την αυτοματοποίηση των πολιτικών του χώρου εργασίας σας.',
                 cta: 'Προσθήκη κανόνα AI',
             },
-            expenseDefaultType: {
-                title: 'Εφαρμογή προεπιλογών εξόδων',
-                subtitle: 'Σε τι πρέπει να αντιστοιχεί αυτός ο κανόνας;',
-                merchant: 'Έμπορος',
-                merchantDescription: 'Ενημέρωση πεδίων σε δαπάνες από συγκεκριμένο έμπορο',
-                category: 'Κατηγορία',
-                categoryDescription: 'Ορίστε προεπιλεγμένο φορολογικό συντελεστή για μια συγκεκριμένη κατηγορία',
-            },
             taxesDisabledEmptyState: {
                 title: 'Οι φόροι δεν είναι ενεργοποιημένοι',
                 subtitle: 'Ενεργοποιήστε τους φόρους για να τεκμηριώσετε και να διεκδικήσετε τους επιλέξιμους φόρους.',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index f7643a27..9b4076a9 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -8557,19 +8557,8 @@ ${reportName}`,
                 ifAnyExpenseMatches: 'Si algún gasto coincide con:',
                 thenApplyFollowingDefaults: 'Luego aplica las siguientes opciones predeterminadas:',
                 confirmErrorCategory: 'Selecciona una categoría',
-                confirmErrorCategoryTax: 'Selecciona una tasa de impuesto',
                 confirmErrorCondition: 'Por favor, introduce un comercio',
                 confirmErrorConditionAndDefault: 'Introduce un comercio y aplica al menos un valor predeterminado',
-                turnOnTaxesFirstTitle: 'Activa primero los impuestos',
-                turnOnTaxesFirstPrompt:
-                    'Las reglas de categoría establecen una tasa de impuesto predeterminada. Activa los impuestos en la configuración de tu espacio de trabajo para usarlos.',
-                categoryRulesApplyGoingForwardTitle: 'Las reglas de categoría se aplican de ahora en adelante',
-                categoryRulesApplyGoingForwardPrompt: 'Se aplica una tasa de impuesto predeterminada a los nuevos gastos de esta categoría. Los gastos que ya existen no cambiarán.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'El tipo impositivo seleccionado es ahora el predeterminado de tu espacio de trabajo, por lo que esta regla ya no es válida. Elige un tipo impositivo diferente.',
-                addTaxRateFirstTitle: 'Añade primero una tasa de impuesto',
-                addTaxRateFirstPrompt:
-                    'Las reglas de categoría establecen una tasa de impuestos predeterminada. Añade una tasa de impuestos distinta de la predeterminada de tu espacio de trabajo para poder usarlas.',
                 createRuleFromExpenseAction: 'Crear una norma',
                 createRuleFromExpensePrompt: 'para aplicar tus cambios a todos los gastos que coincidan con tus criterios.',
             },
@@ -8859,6 +8848,7 @@ ${reportName}`,
                 merchantIs: (merchant: string) => `El comercio es «${merchant}»`,
                 merchantTypeIs: (merchantType: string) => `El tipo de comercio es "${merchantType}"`,
                 categoryIs: (category: string) => `La categoría es «${category}»`,
+                tagIs: (tag: string) => `La etiqueta es «${tag}»`,
             },
             merchantTypeRule: {merchantType: 'Tipo de comercio', saveRule: 'Guardar regla', confirmErrorCategory: 'Por favor, selecciona una categoría.'},
             requireFieldsTable: {
@@ -8928,14 +8918,6 @@ ${reportName}`,
                 cta: 'Añadir regla de IA',
             },
             categoriesDisabledEmptyState: {title: 'Las categorías no están habilitadas', subtitle: 'Habilita las categorías para tener más control sobre tus gastos.'},
-            expenseDefaultType: {
-                title: 'Aplicar valores predeterminados de gastos',
-                subtitle: '¿Con qué debería coincidir esta regla?',
-                merchant: 'Comercio',
-                merchantDescription: 'Actualizar campos en gastos de un comerciante específico',
-                category: 'Categoría',
-                categoryDescription: 'Establece una tasa de impuesto predeterminada para una categoría específica',
-            },
             taxesDisabledEmptyState: {
                 title: 'Los impuestos no están habilitados',
                 subtitle: 'Habilita los impuestos para documentar y recuperar los impuestos que sean deducibles.',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 5b11ce2e..eca16fed 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -8548,19 +8548,8 @@ Rendez obligatoires des informations de dépense comme les reçus et les descrip
                 vendorUnavailable: 'Fournisseur indisponible',
                 supplierUnavailable: 'Fournisseur indisponible',
                 confirmErrorCategory: 'Veuillez sélectionner une catégorie',
-                confirmErrorCategoryTax: 'Veuillez sélectionner un taux de taxe',
                 confirmErrorCondition: 'Veuillez saisir un commerçant',
                 confirmErrorConditionAndDefault: 'Saisissez un commerçant et appliquez au moins un paramètre par défaut',
-                turnOnTaxesFirstTitle: 'Activer d’abord les taxes',
-                turnOnTaxesFirstPrompt: 'Les règles de catégorie définissent un taux de taxe par défaut. Activez les taxes dans les paramètres de votre espace de travail pour les utiliser.',
-                categoryRulesApplyGoingForwardTitle: 'Les règles de catégorie s’appliquent à partir de maintenant',
-                categoryRulesApplyGoingForwardPrompt:
-                    'Un taux de taxe par défaut s’applique aux nouvelles dépenses de cette catégorie. Les dépenses déjà existantes ne seront pas modifiées.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'Le taux de taxe sélectionné est désormais le taux par défaut de votre espace de travail, cette règle n’est donc plus valide. Choisissez un autre taux de taxe.',
-                addTaxRateFirstTitle: 'Ajoutez d’abord un taux de taxe',
-                addTaxRateFirstPrompt:
-                    'Les règles de catégorie définissent un taux de taxe par défaut. Ajoutez un taux de taxe autre que le taux par défaut de votre espace de travail pour les utiliser.',
                 createRuleFromExpenseAction: 'Créer une règle',
                 createRuleFromExpensePrompt: 'pour appliquer vos modifications à toutes les dépenses qui correspondent à vos critères.',
             },
@@ -8877,6 +8866,7 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
                 merchantIs: (merchant: string) => `Le commerçant est « ${merchant} »`,
                 merchantTypeIs: (merchantType: string) => `Le type de commerçant est « ${merchantType} »`,
                 categoryIs: (category: string) => `La catégorie est « ${category} »`,
+                tagIs: (tag: string) => `Le tag est « ${tag} »`,
             },
             merchantTypeRule: {merchantType: 'Type de marchand', saveRule: 'Enregistrer la règle', confirmErrorCategory: 'Veuillez sélectionner une catégorie.'},
             requireFieldsTable: {
@@ -8946,14 +8936,6 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
                 cta: 'Ajouter une règle IA',
             },
             categoriesDisabledEmptyState: {title: 'Les catégories ne sont pas activées', subtitle: 'Activez les catégories pour mieux contrôler vos dépenses.'},
-            expenseDefaultType: {
-                title: 'Appliquer les valeurs de dépense par défaut',
-                subtitle: 'Sur quoi cette règle doit-elle faire correspondre ?',
-                merchant: 'Commerçant',
-                merchantDescription: 'Mettre à jour les champs des dépenses d’un commerçant spécifique',
-                category: 'Catégorie',
-                categoryDescription: 'Définir un taux de taxe par défaut pour une catégorie spécifique',
-            },
             taxesDisabledEmptyState: {title: 'Les taxes ne sont pas activées', subtitle: 'Activez les taxes pour documenter et récupérer les taxes admissibles.', cta: 'Activer les taxes'},
         },
         planTypePage: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index b00d7685..a6f9895e 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -8477,18 +8477,8 @@ Richiedi dettagli sulle spese come ricevute e descrizioni, imposta limiti e valo
                 vendorUnavailable: 'Fornitore non disponibile',
                 supplierUnavailable: 'Fornitore non disponibile',
                 confirmErrorCategory: 'Seleziona una categoria',
-                confirmErrorCategoryTax: "Seleziona un'aliquota fiscale",
                 confirmErrorCondition: 'Inserisci un esercente',
                 confirmErrorConditionAndDefault: 'Inserisci un esercente e applica almeno un valore predefinito',
-                turnOnTaxesFirstTitle: 'Attiva prima le tasse',
-                turnOnTaxesFirstPrompt: 'Le regole di categoria impostano un’aliquota fiscale predefinita. Attiva le imposte nelle impostazioni dello spazio di lavoro per usarle.',
-                categoryRulesApplyGoingForwardTitle: 'Le regole di categoria si applicano da ora in poi',
-                categoryRulesApplyGoingForwardPrompt: 'Un’aliquota fiscale predefinita viene applicata alle nuove spese in questa categoria. Le spese già esistenti non verranno modificate.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'L’aliquota fiscale selezionata è ora quella predefinita del tuo workspace, quindi questa regola non è più valida. Scegli un’altra aliquota fiscale.',
-                addTaxRateFirstTitle: "Aggiungi prima un'aliquota fiscale",
-                addTaxRateFirstPrompt:
-                    'Le regole di categoria impostano un’aliquota fiscale predefinita. Aggiungi un’aliquota diversa da quella predefinita dello spazio di lavoro per poterle usare.',
                 createRuleFromExpenseAction: 'Crea una regola',
                 createRuleFromExpensePrompt: 'per applicare le modifiche a tutte le spese che corrispondono ai tuoi criteri.',
             },
@@ -8805,6 +8795,7 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
                 merchantIs: (merchant: string) => `L'esercente è "${merchant}"`,
                 merchantTypeIs: (merchantType: string) => `Il tipo di esercente è "${merchantType}"`,
                 categoryIs: (category: string) => `La categoria è "${category}"`,
+                tagIs: (tag: string) => `Il tag è "${tag}"`,
             },
             merchantTypeRule: {merchantType: 'Tipo di esercente', saveRule: 'Salva regola', confirmErrorCategory: 'Seleziona una categoria.'},
             requireFieldsTable: {
@@ -8870,14 +8861,6 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
             },
             agentRulesEmptyState: {title: 'Nessuna regola agente aggiunta', subtitle: 'Crea una regola per automatizzare le policy del tuo workspace.', cta: 'Aggiungi regola IA'},
             categoriesDisabledEmptyState: {title: 'Le categorie non sono abilitate', subtitle: 'Attiva le categorie per avere un maggiore controllo sulle tue spese.'},
-            expenseDefaultType: {
-                title: 'Applica impostazioni predefinite spesa',
-                subtitle: 'Su cosa dovrebbe corrispondere questa regola?',
-                merchant: 'Commerciante',
-                merchantDescription: 'Aggiorna i campi delle spese di un determinato esercente',
-                category: 'Categoria',
-                categoryDescription: "Imposta un'aliquota fiscale predefinita per una categoria specifica",
-            },
             taxesDisabledEmptyState: {title: 'Le imposte non sono abilitate', subtitle: 'Abilita le imposte per documentare e recuperare quelle ammissibili.', cta: 'Abilita imposte'},
         },
         planTypePage: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index eef159b8..0501cbc2 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -8370,16 +8370,8 @@ ${reportName}`,
                 vendorUnavailable: 'ベンダーを利用できません',
                 supplierUnavailable: 'サプライヤーを利用できません',
                 confirmErrorCategory: 'カテゴリを選択してください',
-                confirmErrorCategoryTax: '税率を選択してください',
                 confirmErrorCondition: '店舗名を入力してください',
                 confirmErrorConditionAndDefault: '取引先を入力し、少なくとも 1 つのデフォルトを適用してください',
-                turnOnTaxesFirstTitle: '先に税金を有効にしてください',
-                turnOnTaxesFirstPrompt: 'カテゴリルールでは、デフォルトの税率を設定できます。利用するには、ワークスペース設定で税金を有効にしてください。',
-                categoryRulesApplyGoingForwardTitle: 'カテゴリルールは今後に適用されます',
-                categoryRulesApplyGoingForwardPrompt: 'このカテゴリーの新しい経費には、デフォルトの税率が適用されます。既存の経費は変更されません。',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault: '選択した税率がワークスペースのデフォルトになっているため、このルールは無効になりました。別の税率を選択してください。',
-                addTaxRateFirstTitle: '先に税率を追加してください',
-                addTaxRateFirstPrompt: 'カテゴリルールではデフォルトの税率が設定されます。ワークスペースのデフォルト以外の税率を追加してから、カテゴリルールを使用してください。',
                 createRuleFromExpenseAction: 'ルールを作成',
                 createRuleFromExpensePrompt: '条件に一致するすべての経費に変更を適用します。',
             },
@@ -8694,6 +8686,7 @@ ${reportName}`,
                 merchantIs: (merchant: string) => `加盟店名は「${merchant}」です`,
                 merchantTypeIs: (merchantType: string) => `加盟店タイプ: 「${merchantType}」`,
                 categoryIs: (category: string) => `カテゴリは「${category}」です`,
+                tagIs: (tag: string) => `タグは「${tag}」です`,
             },
             merchantTypeRule: {merchantType: '加盟店種別', saveRule: 'ルールを保存', confirmErrorCategory: 'カテゴリを選択してください。'},
             requireFieldsTable: {
@@ -8759,14 +8752,6 @@ ${reportName}`,
             },
             agentRulesEmptyState: {title: 'エージェントルールが追加されていません', subtitle: 'ワークスペースのポリシーを自動化するルールを作成します。', cta: 'AIルールを追加'},
             categoriesDisabledEmptyState: {title: 'カテゴリが有効になっていません', subtitle: 'カテゴリを有効にして、支出をより細かく管理しましょう。'},
-            expenseDefaultType: {
-                title: '経費のデフォルトを適用',
-                subtitle: 'このルールは何に一致させますか?',
-                merchant: '加盟店',
-                merchantDescription: '特定の加盟店の経費フィールドを更新',
-                category: 'カテゴリ',
-                categoryDescription: '特定のカテゴリにデフォルトの税率を設定する',
-            },
             taxesDisabledEmptyState: {title: '税金は有効になっていません', subtitle: '税金を有効にして、対象となる税金を記録し還付請求できるようにします。', cta: '税金を有効にする'},
         },
         planTypePage: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 34c05ae7..ac44c34b 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -8453,17 +8453,8 @@ Vereis onkostendetails zoals bonnen en beschrijvingen, stel limieten en standaar
                 vendorUnavailable: 'Leverancier niet beschikbaar',
                 supplierUnavailable: 'Leverancier niet beschikbaar',
                 confirmErrorCategory: 'Selecteer een categorie',
-                confirmErrorCategoryTax: 'Selecteer een belastingtarief',
                 confirmErrorCondition: 'Voer een leverancier in',
                 confirmErrorConditionAndDefault: 'Voer een leverancier in en stel minstens één standaard in',
-                turnOnTaxesFirstTitle: 'Zet eerst belastingen aan',
-                turnOnTaxesFirstPrompt: 'Categorischregels stellen een standaardbelastingtarief in. Schakel belastingen in bij de instellingen van je workspace om ze te gebruiken.',
-                categoryRulesApplyGoingForwardTitle: 'Categoriegregels gelden vanaf nu',
-                categoryRulesApplyGoingForwardPrompt: 'Een standaardbelastingtarief is van toepassing op nieuwe uitgaven in deze categorie. Bestaande uitgaven veranderen niet.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'Het geselecteerde belastingtarief is nu de standaard voor je werkruimte, waardoor deze regel niet meer geldig is. Kies een ander belastingtarief.',
-                addTaxRateFirstTitle: 'Voeg eerst een belastingtarief toe',
-                addTaxRateFirstPrompt: 'Categorielimieten stellen een standaardbelastingtarief in. Voeg een ander belastingtarief toe dan de standaard van je werkruimte om ze te gebruiken.',
                 createRuleFromExpenseAction: 'Maak een regel',
                 createRuleFromExpensePrompt: 'om je wijzigingen toe te passen op alle onkosten die aan je criteria voldoen.',
             },
@@ -8779,6 +8770,7 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
                 merchantIs: (merchant: string) => `Handelaar is "${merchant}"`,
                 merchantTypeIs: (merchantType: string) => `Type leverancier is "${merchantType}"`,
                 categoryIs: (category: string) => `Categorie is "${category}"`,
+                tagIs: (tag: string) => `Tag is "${tag}"`,
             },
             merchantTypeRule: {merchantType: 'Type handelaar', saveRule: 'Regel opslaan', confirmErrorCategory: 'Selecteer een categorie.'},
             requireFieldsTable: {
@@ -8844,14 +8836,6 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
             },
             agentRulesEmptyState: {title: 'Geen agentregels toegevoegd', subtitle: 'Maak een regel om je werkruimtebeleid te automatiseren.', cta: 'AI-regel toevoegen'},
             categoriesDisabledEmptyState: {title: 'Categorieën zijn niet ingeschakeld', subtitle: 'Schakel categorieën in om meer controle te krijgen over je uitgaven.'},
-            expenseDefaultType: {
-                title: 'Standaardwaarden voor uitgaven toepassen',
-                subtitle: 'Waar moet deze regel op overeenkomen?',
-                merchant: 'Winkelier',
-                merchantDescription: 'Veldwaarden bij uitgaven van een specifieke verkoper bijwerken',
-                category: 'Categorie',
-                categoryDescription: 'Stel een standaardbelastingtarief in voor een specifieke categorie',
-            },
             taxesDisabledEmptyState: {
                 title: 'Belastingen zijn niet ingeschakeld',
                 subtitle: 'Activeer belastingen om in aanmerking komende belastingen vast te leggen en terug te vorderen.',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index f128ab32..4f93292d 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -8477,17 +8477,8 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
                 vendorUnavailable: 'Dostawca niedostępny',
                 supplierUnavailable: 'Dostawca niedostępny',
                 confirmErrorCategory: 'Wybierz kategorię',
-                confirmErrorCategoryTax: 'Wybierz stawkę podatku',
                 confirmErrorCondition: 'Wpisz sprzedawcę',
                 confirmErrorConditionAndDefault: 'Wpisz sprzedawcę i zastosuj co najmniej jedną domyślną opcję',
-                turnOnTaxesFirstTitle: 'Najpierw włącz podatki',
-                turnOnTaxesFirstPrompt: 'Reguły kategorii ustawiają domyślną stawkę podatku. Włącz podatki w ustawieniach swojego workspace, aby z nich korzystać.',
-                categoryRulesApplyGoingForwardTitle: 'Reguły kategorii będą stosowane od teraz',
-                categoryRulesApplyGoingForwardPrompt: 'Domyślna stawka podatku będzie stosowana do nowych wydatków w tej kategorii. Istniejące wydatki nie zostaną zmienione.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'Wybrana stawka podatku jest teraz domyślną stawką w twoim zespole, więc ta reguła nie jest już prawidłowa. Wybierz inną stawkę podatku.',
-                addTaxRateFirstTitle: 'Najpierw dodaj stawkę podatku',
-                addTaxRateFirstPrompt: 'Reguły kategorii ustawiają domyślną stawkę podatku. Dodaj stawkę podatku inną niż domyślna w twoim obszarze roboczym, żeby z nich korzystać.',
                 createRuleFromExpenseAction: 'Utwórz regułę',
                 createRuleFromExpensePrompt: 'aby zastosować zmiany do wszystkich wydatków spełniających twoje kryteria.',
             },
@@ -8804,6 +8795,7 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
                 merchantIs: (merchant: string) => `Sprzedawca to „${merchant}”`,
                 merchantTypeIs: (merchantType: string) => `Typ sprzedawcy to „${merchantType}"`,
                 categoryIs: (category: string) => `Kategoria to „${category}”`,
+                tagIs: (tag: string) => `Tag to „${tag}”`,
             },
             merchantTypeRule: {merchantType: 'Typ sprzedawcy', saveRule: 'Zapisz regułę', confirmErrorCategory: 'Wybierz kategorię.'},
             requireFieldsTable: {
@@ -8869,14 +8861,6 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
             },
             agentRulesEmptyState: {title: 'Nie dodano reguł agenta', subtitle: 'Utwórz regułę, żeby zautomatyzować zasady swojego workspace’u.', cta: 'Dodaj regułę AI'},
             categoriesDisabledEmptyState: {title: 'Kategorie nie są włączone', subtitle: 'Włącz kategorie, żeby mieć większą kontrolę nad wydatkami.'},
-            expenseDefaultType: {
-                title: 'Zastosuj domyślne ustawienia wydatków',
-                subtitle: 'Do czego ta reguła powinna pasować?',
-                merchant: 'Sprzedawca',
-                merchantDescription: 'Zaktualizuj pola w wydatkach od konkretnego sprzedawcy',
-                category: 'Kategoria',
-                categoryDescription: 'Ustaw domyślną stawkę podatku dla konkretnej kategorii',
-            },
             taxesDisabledEmptyState: {title: 'Podatki nie są włączone', subtitle: 'Włącz podatki, aby udokumentować i odzyskać kwalifikujące się podatki.', cta: 'Włącz podatki'},
         },
         planTypePage: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index c031b987..9afc727f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -8459,17 +8459,8 @@ Exija dados de despesas como recibos e descrições, defina limites e padrões e
                 vendorUnavailable: 'Fornecedor indisponível',
                 supplierUnavailable: 'Fornecedor indisponível',
                 confirmErrorCategory: 'Selecione uma categoria',
-                confirmErrorCategoryTax: 'Selecione uma alíquota de imposto',
                 confirmErrorCondition: 'Insira um estabelecimento',
                 confirmErrorConditionAndDefault: 'Insira um comerciante e aplique pelo menos um padrão',
-                turnOnTaxesFirstTitle: 'Ative os impostos primeiro',
-                turnOnTaxesFirstPrompt: 'As regras de categoria definem uma alíquota de imposto padrão. Ative os impostos nas configurações do seu workspace para usá-los.',
-                categoryRulesApplyGoingForwardTitle: 'As regras de categoria se aplicam daqui em diante',
-                categoryRulesApplyGoingForwardPrompt: 'Uma taxa de imposto padrão se aplica às novas despesas desta categoria. As despesas que já existem não serão alteradas.',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault:
-                    'A alíquota de imposto selecionada agora é o padrão do seu workspace, então esta regra não é mais válida. Escolha uma alíquota de imposto diferente.',
-                addTaxRateFirstTitle: 'Adicione uma alíquota de imposto primeiro',
-                addTaxRateFirstPrompt: 'As regras de categoria definem uma taxa de imposto padrão. Adicione uma taxa diferente da padrão do seu espaço de trabalho para usá-las.',
                 createRuleFromExpenseAction: 'Criar regra',
                 createRuleFromExpensePrompt: 'para aplicar suas alterações a todas as despesas que correspondem aos seus critérios.',
             },
@@ -8786,6 +8777,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
                 merchantIs: (merchant: string) => `O comerciante é "${merchant}"`,
                 merchantTypeIs: (merchantType: string) => `O tipo de estabelecimento é "${merchantType}"`,
                 categoryIs: (category: string) => `A categoria é "${category}"`,
+                tagIs: (tag: string) => `A etiqueta é "${tag}"`,
             },
             merchantTypeRule: {merchantType: 'Tipo de comerciante', saveRule: 'Salvar regra', confirmErrorCategory: 'Selecione uma categoria.'},
             requireFieldsTable: {
@@ -8851,14 +8843,6 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
             },
             agentRulesEmptyState: {title: 'Nenhuma regra de agente adicionada', subtitle: 'Crie uma regra para automatizar as políticas do seu workspace.', cta: 'Adicionar regra de IA'},
             categoriesDisabledEmptyState: {title: 'Categorias não estão ativadas', subtitle: 'Ative categorias para ter mais controle sobre seus gastos.'},
-            expenseDefaultType: {
-                title: 'Aplicar padrões de despesa',
-                subtitle: 'Com o que esta regra deve corresponder?',
-                merchant: 'Comerciante',
-                merchantDescription: 'Atualizar campos em despesas de um comerciante específico',
-                category: 'Categoria',
-                categoryDescription: 'Definir uma taxa de imposto padrão para uma categoria específica',
-            },
             taxesDisabledEmptyState: {title: 'Impostos não estão ativados', subtitle: 'Ative impostos para documentar e recuperar impostos elegíveis.', cta: 'Ativar impostos'},
         },
         planTypePage: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 074df21d..3253f6e8 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -8165,16 +8165,8 @@ ${reportName}`,
                 vendorUnavailable: '供应商不可用',
                 supplierUnavailable: '供应商不可用',
                 confirmErrorCategory: '请选择一个类别',
-                confirmErrorCategoryTax: '请选择税率',
                 confirmErrorCondition: '请输入商家',
                 confirmErrorConditionAndDefault: '输入商家,并至少应用一个默认设置',
-                turnOnTaxesFirstTitle: '请先启用税费',
-                turnOnTaxesFirstPrompt: '类别规则会设置默认税率。请在工作区设置中启用税费以使用此功能。',
-                categoryRulesApplyGoingForwardTitle: '类别规则将从现在起生效',
-                categoryRulesApplyGoingForwardPrompt: '此类别中的新报销将应用默认税率,已存在的报销不会改变。',
-                confirmErrorCategoryTaxMoveIsWorkspaceDefault: '所选税率现在已成为您的工作区默认税率,因此此规则不再有效。请选择其他税率。',
-                addTaxRateFirstTitle: '请先添加税率',
-                addTaxRateFirstPrompt: '类别规则会设置一个默认税率。请添加一个不同于工作区默认值的税率以使用类别规则。',
                 createRuleFromExpenseAction: '创建规则',
                 createRuleFromExpensePrompt: '将您的更改应用于所有符合条件的报销。',
             },
@@ -8476,6 +8468,7 @@ ${reportName}`,
                 merchantIs: (merchant: string) => `商户为“${merchant}”`,
                 merchantTypeIs: (merchantType: string) => `商户类型为"${merchantType}"`,
                 categoryIs: (category: string) => `类别为“${category}”`,
+                tagIs: (tag: string) => `标签为“${tag}”`,
             },
             merchantTypeRule: {merchantType: '商户类型', saveRule: '保存规则', confirmErrorCategory: '请选择一个类别。'},
             requireFieldsTable: {
@@ -8533,14 +8526,6 @@ ${reportName}`,
             },
             agentRulesEmptyState: {title: '未添加代理规则', subtitle: '创建规则以自动化您的工作区策略。', cta: '添加 AI 规则'},
             categoriesDisabledEmptyState: {title: '类别未启用', subtitle: '启用类别以更好地控制您的支出。'},
-            expenseDefaultType: {
-                title: '应用报销默认值',
-                subtitle: '此规则应匹配什么内容?',
-                merchant: '商户',
-                merchantDescription: '更新来自特定商家的报销项目字段',
-                category: '类别',
-                categoryDescription: '为特定类别设置默认税率',
-            },
             taxesDisabledEmptyState: {title: '税费未启用', subtitle: '启用税费以记录并追回符合条件的税款。', cta: '启用税费'},
         },
         planTypePage: {

Note

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

View workflow run

@JmillsExpensify
JmillsExpensify self-requested a review October 19, 2026 17:37
Signed-off-by: krishna2323 <[email protected]>
@Krishna2323
Krishna2323 marked this pull request as ready for review October 19, 2026 18:24
@Krishna2323
Krishna2323 requested review from a team as code owners October 19, 2026 18:24
Comment thread src/libs/ExpenseDefaultRuleUtils.ts Outdated
@situchan

Copy link
Copy Markdown
Contributor

This is not testable yet right?

Basic test failing (rule not applied to expense) maybe because backend is not deployed yet?

bug.mov

@situchan

Copy link
Copy Markdown
Contributor

Bug: Infinite loading after saving a rule opened from category settings

Screen.Recording.2026-10-19.at.9.14.24.PM.mov

@situchan

Copy link
Copy Markdown
Contributor

There are many cases showing not found page in category settings flow

Screen.Recording.2026-10-19.at.9.23.23.PM.mov

@situchan

Copy link
Copy Markdown
Contributor

Bug: category rule created in production shows in Merchants section and not viewable/editable

Screen.Recording.2026-10-19.at.9.46.35.PM.mov
Screenshot 2026-10-19 at 9 49 59 PM

@github-actions

Copy link
Copy Markdown
Contributor

🚧 JmillsExpensify has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@JmillsExpensify

Copy link
Copy Markdown
Contributor

I also had trouble testing it. What beta do I need to be on?

@JS00001

JS00001 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@situchan which email are you using for this one? I can add you to the beta

@JS00001

JS00001 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@JmillsExpensify I added you to the beta, it may take up to an hour. I also requested ring1 add you to the category defaults beta. Until that happens, you can test expense defaults with a merchant or tag filter

@situchan

Copy link
Copy Markdown
Contributor

@situchan which email are you using for this one? I can add you to the beta

[email protected]
[email protected]
[email protected]
[email protected]

@Krishna2323

Krishna2323 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

@JS00001 please add these also:

  1. [email protected]
  2. [email protected]
  3. [email protected]

When I tested it was working without beta 🤔

@JS00001

JS00001 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

We've just enabled one of the beta for all customers. This means that application should now work when matching on either a merchant, or tag. We are still working through the category one. Bear with me, we'll get to that in an hour or two

@JmillsExpensify

Copy link
Copy Markdown
Contributor

Cool, let me know. Probably makes more sense to jump in then.

@JS00001

JS00001 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@JmillsExpensify this should be good to test now

@Krishna2323

Copy link
Copy Markdown
Contributor Author

Bug: Infinite loading after saving a rule opened from category settings

Fixed in b972724. Saving from category settings navigated back to the page it was already on, so the spinner never cleared. It now returns to category settings.

There are many cases showing not found page in category settings flow

Same cause, same commit. The editor built expense-flow routes that the category screens weren't registered for, which is why only Tax worked. Every row resolves now.

Bug: category rule created in production shows in Merchants section and not viewable/editable

Two bugs, both fixed. Category-only rules now group under Categories (b972724).

The row wasn't responding to taps because rules created by the backend have an extra trigger, UpdateTransaction, which our editor rejected. It's now accepted and preserved on save (e13a245).

@Krishna2323

Copy link
Copy Markdown
Contributor Author

@JS00001 Two things:

  1. Should the client set UpdateTransaction on new rules now, or wait for the Release 3 auth PR? Right now, ours only fire on creation, unlike backend-created rules. Either way, the editor now preserves the trigger when saving an existing rule.
  2. Does the backend update category names in rule filters and tax externalIDs when a rate's code changes? I'm updating both optimistically, assuming it does.

Also, getCategoryTaxDetails is now the only client-side optimistic rule application left. So category tax updates feel instant, while merchant, tag, and amount wait for the server. Should we extend this to all of them or remove it?

@Krishna2323

Copy link
Copy Markdown
Contributor Author

The failing check isn't related, re-run might fix that.

@github-actions

Copy link
Copy Markdown
Contributor

🚧 JmillsExpensify has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

Copy link
Copy Markdown
Contributor

🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
Built from App PR #104155.

Android 🤖 iOS 🍎
⏩ SKIPPED ⏩ ⏩ SKIPPED ⏩
The build for Android was skipped The build for iOS was skipped
Web 🕸️
https://104155.pr-testing.expensify.com
Web

👀 View the workflow run that generated this build 👀

@JmillsExpensify

Copy link
Copy Markdown
Contributor

One thing I noticed in testing. We don't clarify contains vs exactly matches in the rule summary. Accordingly, these two look like duplicates but aren't actually duplicates.

CleanShot 2026-10-21 at 14 52 13@2x

@JmillsExpensify

Copy link
Copy Markdown
Contributor

Would recommend we accommodate this and go with:

  • Merchant contains...
  • Merchant exactly matches...

@JS00001

JS00001 commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Should the client set UpdateTransaction on new rules now, or wait for the Release 3 auth PR? Right now, ours only fire on creation, unlike backend-created rules. Either way, the editor now preserves the trigger when saving an existing rule.

Can you explain this please? Is this for category defaults?

Does the backend update category names in rule filters and tax externalIDs when a rate's code changes? I'm updating both optimistically, assuming it does.

No, we dont. In the future we will show a 'stale rule' violation

Also, getCategoryTaxDetails is now the only client-side optimistic rule application left. So category tax updates feel instant, while merchant, tag, and amount wait for the server. Should we extend this to all of them or remove it?

Hmm, In the follow up, we will add even more matching, so lets not do this optimistically

Copy link
Copy Markdown
Contributor

Btw, one other thing I noticed. It doesn't look like we are handling the audit logs properly. This is what I saw after my changes.

CleanShot 2026-10-21 at 15.53.47@2x.png

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants