Skip to content

[Rules Revamp] Release 2: Move category-based creation into Rules; add Require fields and Flag for review#94696

Merged
luacmartins merged 70 commits into
Expensify:mainfrom
Krishna2323:krishna2323/issue-92741
Jul 1, 2026
Merged

[Rules Revamp] Release 2: Move category-based creation into Rules; add Require fields and Flag for review#94696
luacmartins merged 70 commits into
Expensify:mainfrom
Krishna2323:krishna2323/issue-92741

Conversation

@Krishna2323

@Krishna2323 Krishna2323 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Fixed Issues

$ #92741
PROPOSAL:

Tests

Prerequisites

  • Use a workspace with Rules enabled (More features → Rules).
  • Enable rulesRevamp beta on the test account.
    • Web dev console: Onyx.merge('betas', ['rulesRevamp'])
  • Workspace should have Categories enabled with at least 2–3 categories (e.g. Meals and Entertainment, Travel).
  • Optional: enable Expensify Card for Card restrictions tab tests; enable Attendee tracking for require-attendees tests.

Rules hub — tabs & navigation

  1. Open Workspace settings → Rules.
  2. Verify tabs appear: General, Card restrictions, Expense defaults, Require fields, Flag for review.
  3. Switch between tabs — each loads without crash or console errors.
  4. Verify Card restrictions tab loads without Maximum update depth exceeded / FlashList crash (previously broken).

+ Rule creation flow (4 options)

  1. On any table tab, click + Rule (or Add rule).
  2. Verify New rule RHP shows 4 options in order:
    • Restrict card spend
    • Flag for review
    • Require fields
    • Apply expense defaults
  3. Tap each option — verify it opens the correct creation flow and back navigation works.

Require fields tab

Empty state

  1. Use a workspace with no category require-fields rules.
  2. Open Require fields tab.
  3. Verify empty state: title, subtitle, and Create require rule CTA.
  4. Tap CTA → opens Require fields creation flow.

Create & table

  1. + Rule → Require fields → pick a category → enable Description (and/or Receipt) → Save.
  2. Verify rule appears on Require fields tab with:
    • Type: Require fields
    • Condition: Category is "<name>"
    • Rule: combined text for all enabled fields (e.g. Require description, always require receipt) — one row per category
  3. Create rules on a second category — verify two rows.

Edit

  1. Tap a row → opens single editor with all toggles for that category.
  2. Change toggles → Save → table updates.

Create dedup (categories with existing rules)

  1. + Rule → Require fields → open category picker.
  2. Verify categories that already have require-fields rules are still listed (not hidden).
  3. Select one → form pre-fills existing settings → save updates the same row.

Bulk delete

  1. Select one or more rows → Delete from bulk actions.
  2. Confirm → all require-fields settings for those categories are cleared; rows removed from table.

Search (if enough rows)

  1. Use Find rule search — verify filtering by category / rule text works.

Flag for review tab

Empty state

  1. Workspace with no category flag rules → open Flag for review tab.
  2. Verify empty state + Create flag rule CTA.

Create & table

  1. + Rule → Flag for review → pick category → set amount → choose Individual expense → Save.
  2. Verify row: Type Flag, Condition Category is "…" and amount above $X, Rule Flag for review.
  3. Create another rule with Category total limit type.
  4. Verify condition copy uses daily category total above $X (not “daily total above”).

Create dedup

  1. + Rule → Flag for review → category picker.
  2. Verify categories that already have a flag amount rule are still listed.

Edit & delete

  1. Tap row → edit amount / limit type → Save → table updates.
  2. Bulk-select → Delete → rule removed.

Expense defaults — Merchant types

  1. Open Expense defaults tab.
  2. With no custom merchant rules, verify 12 locked Merchant type rows show as a flat list (Gas, Hotel, Taxi, etc.) with Update pill and lock icon on hover/tooltip (common.locked).
  3. Tap a locked row (e.g. Gas) → Merchant type condition is locked; Category is editable → change category → Save → table updates.
  4. Add a custom merchant rule (+ Rule → Apply expense defaults).
  5. Verify table splits into Merchants and Merchant types subsection headers.

Category settings migration (RULES_REVAMP beta)

Beta ON

  1. Open Workspace → Categories → [category] → Settings.
  2. Verify Category rules section still shows: Approver, Default tax rate (if tax on), Description hint.
  3. Verify these are hidden: Flag amounts over, Require receipts over, Require itemized receipts over, Require fields.

Beta OFF (regression)

  1. Disable rulesRevamp beta → reload.
  2. Open same category settings → verify legacy rule menus are visible again.

Selection & bulk actions (table tabs)

  1. On Require fields, Flag for review, Card restrictions, or Expense defaults — select rows via checkbox.
  2. Verify bulk Delete appears; + Rule hides while selection active (narrow + desktop behavior per R1).
  3. Deselect all → + Rule returns.
  4. Switch tabs → selection clears.

Card restrictions — lock tooltip (web)

  1. Open Card restrictions tab with default “All cards” row.
  2. Hover lock icon on default row — tooltip shows without BoundsObserver / ref console errors.

Regression (R1 still works)

  1. General tab — policy toggles/cards still navigate correctly.
  2. Card restrictions — create/edit/delete custom spend rule still works.
  3. Expense defaults — custom merchant rules create/edit/delete still works.

  • Verify that no errors appear in the JS console

Offline tests

  1. Go offline.
  2. Create or edit a Require fields / Flag for review rule — verify optimistic UI / pending state.
  3. Go online — verify sync completes without errors.

QA Steps

  • Same as tests

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
    • 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_hybrid.mp4
Android: mWeb Chrome
android_mWeb.mp4
iOS: Native
ios_hybrid.mp4
iOS: mWeb Safari
ios_mWeb.mp4
MacOS: Chrome / Safari
web_chrome.mp4

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@melvin-bot

melvin-bot Bot commented Jun 26, 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 ts-node ./scripts/generateTranslations.ts --help

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

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@Krishna2323 Krishna2323 marked this pull request as ready for review June 26, 2026 15:46
@Krishna2323 Krishna2323 requested review from a team as code owners June 26, 2026 15:46
@melvin-bot melvin-bot Bot requested review from flaviadefaria and situchan June 26, 2026 15:46
@melvin-bot

melvin-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

@situchan Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot melvin-bot Bot removed the request for review from a team June 26, 2026 15:46
@melvin-bot melvin-bot Bot requested a review from luacmartins July 1, 2026 19:02
@situchan

situchan commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

One minor issue: Description, Attendees toggled on persists when select category which already has a rule.

Repro step:

  1. Create rule (Description toggle on) with category A
  2. Add rule > Required fields
  3. Select category A
  4. Observe that Description is automatically toggled on
  5. Close RHP
  6. Add rule > Required fields
  7. Select category B
  8. Switch category to A
  9. Observe that Description is not automatically toggled on
Screen.Recording.2026-07-01.at.8.03.03.PM.mov

Similar issue (with the same root cause) happens on Flag for review

Screen.Recording.2026-07-01.at.8.10.44.PM.mov

@situchan situchan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Both bugs share a root cause: on the "New" rule flow, the category picker doesn't reconcile the draft with the newly-selected category's existing rule.

  1. Require fields — switching category leaks stale toggles and overwrites the new category's config.
    RequireFieldsRuleCategoryPageBase.onSave builds {...form, category: B} and passes it through getEffectiveRequireFieldsRuleForm, whose precedence is draft ?? category ?? false.
    So if you pick Category A, toggle Description on, then reopen the picker and switch to Category B, A's requireDescription: true beats B's real value. B is then shown — and on Save persisted (setPolicyCategoryDescriptionRequired(B, true)) — with A's toggles, silently clobbering B's existing require-fields settings.
    The Edit flow re-seeds from the category and locks the picker; the New flow never re-seeds.

  2. Flag for review — picking a category that already has a rule shows a blank amount and silently overwrites it.
    FlagForReviewRuleCategoryPageBase.onSave writes only {CATEGORY: value} — it never seeds MAX_EXPENSE_AMOUNT / EXPENSE_LIMIT_TYPE from the selected category, and FlagForReviewRulePageBase's New-flow init seeds an empty {} and returns early.
    Existing-rule categories are still listed in the picker (per the PR's dedup requirement), so selecting one shows an empty amount field and Save overwrites the existing threshold with no indication a rule already existed.
    This is asymmetric with the Require-fields picker (which does seed via getEffectiveRequireFieldsRuleForm).

{/*
* Rules Revamp R2: category-based rule creation moves to Workspace > Rules.
* When removing the RULES_REVAMP beta, delete this entire block (and the related useMemo
* values above) instead of keeping the legacy Category settings entry points.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we just add a link to the issue that’ll remove this block please?

@luacmartins luacmartins merged commit 0b63e75 into Expensify:main Jul 1, 2026
42 checks passed
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

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

@OSBotify

OSBotify commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

OSBotify commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/luacmartins in version: 9.4.27-1 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Reviewed this PR for Help site impact — yes, changes are needed, and I've opened a draft PR for them.

What this PR changes (user-facing): It reorganizes the workspace Rules hub into tabs (General, Card restrictions, Expense defaults, Require fields, Flag for review), adds a + RuleNew rule creation flow with four options, introduces two new category-based rule types (Require fields and Flag for review), and moves category-based rule creation (Flag amounts / Require fields) out of individual category settings and into the Rules hub.

Docs affected: Workspace-Rules.md currently documents the pre-revamp Rules page, so it needs updating to describe the new tabbed hub and the two new rule types.

Draft help site PR: #95297 — updates Workspace-Rules.md with the new tab structure, the + Rule flow, and sections for Require fields and Flag for review rules. All labels are taken verbatim from the workspace.rules strings added in this PR, following the HelpDot naming/authoring guidelines.

⚠️ Heads up: this feature is gated behind the rulesRevamp beta and isn't GA yet, so most workspaces still see the old Rules page. I've kept the docs PR as a draft and flagged that it should be held until the beta rolls out to everyone. I also left the existing pre-revamp sections in place (they still describe current GA behavior) rather than rewriting them prematurely.

@Krishna2323, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR Ready for review

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #95305 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #95352 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #95353 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

This PR failing because of the issue #95353
This issue is reproducible in: All platforms

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #95354 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #95357 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #95358 was identified to be related to this PR.

@OSBotify

OSBotify commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/grgia in version: 9.4.27-2 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

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.

10 participants