Background
R1 (#92739) centralized workspace merchant rules into the Expense defaults tab, and R2 (#92741) added the 12 locked Merchant types defaults. Every rule in that tab matches on a merchant or a merchant type.
Classic lets an admin set a default tax rate on a category, and we have no equivalent on the new platform. Any workspace using that setting today loses it when they move over, which makes this a backward-compatibility gap and a blocker on removing the beta.
This issue adds Category as a second condition for Apply expense defaults, so an admin can set a default tax rate for a category. Expense defaults has not yet been migrated to the new rules system, so the rule carries real constraints: one condition per rule, tax as the only available default, and no retroactive application. We express all three with the lock affordance R1 and R2 already established.
Scope
- Add
Category as a second condition to the Apply expense defaults branch of New rule, alongside the existing Merchant condition.
- Allow exactly one condition per rule. Setting one locks the other, because
Expense defaults has not been migrated to the new rules system.
- When the condition is
Category, allow only Tax in the defaults to apply and lock every other field.
- Lock
Apply to existing unsubmitted expenses for category rules, since a category tax default only applies to expenses created after the rule is saved.
- Show category rules in a new
Categories subsection of the Expense defaults table.
- No change to the
New rule option list or its copy. This extends the existing Apply expense defaults branch; it does not add a fifth rule type.
Product and design details
Applicability
The Category condition is available to workspace admins when taxes are enabled on the workspace. Taxes are what the rule sets, so with taxes off there is nothing to configure. In that case the Category condition row renders locked, and the Merchant condition continues to work as it does today.
Condition: one of Merchant or Category
The If any expense matches: section has two rows, Merchant and Category, in that order. Each shows its selected value beneath its label, and nothing beneath it when unset.
Setting a value in one row locks the other:
- The locked row replaces its chevron with the lock icon and no longer opens a picker.
- Selecting the locked row opens the existing centered-modal explainer. Reuse the R1 modal pattern; no new component.
- The explainer is informational only, with a single
Got it action. It changes no selections.
Explainer copy, which differs by direction:
Category locked, because a merchant is set. Heading: Only one condition per rule. Body: This rule already matches on a merchant. Reset the rule to match on a category instead.
Merchant locked, because a category is set. Heading: Only one condition per rule. Body: This rule already matches on a category. Reset the rule to match on a merchant instead.
Category locked, because taxes are disabled. Heading: Turn on taxes first. Body: Category rules set a default tax rate. Turn on taxes in your workspace settings to use them.
Reset appears in the RHP header once either condition is set. It clears both conditions and every default, and unlocks every row. It does not appear on a rule with nothing selected.
Defaults to apply
The Then apply the following defaults: section lists the sum of all conditions that apply to Merchant and Category. Which rows are available depends on the condition:
- Condition is
Merchant: every row is enabled. This is today's behavior and it does not change.
- Condition is
Category: only Tax is enabled. Every other row shows the lock icon and opens the explainer when selected.
- No condition set yet: every row is enabled, and rows lock as soon as a category is chosen.
Explainer copy for a row locked by the category condition. Heading: Only tax is available for category rules. Body: Category rules can set a default tax rate. To set other defaults, match on a merchant instead.
An admin can select multiple categories to set tax defaults in bulk. The category picker excludes any category that already has one.
Apply to existing unsubmitted expenses and Preview matches
For merchant rules, both controls behave exactly as they do today.
For category rules:
- The toggle is locked in the off position and shows the lock icon in place of the switch. Selecting it opens the explainer. Heading:
Category rules apply going forward. Body: A default tax rate applies to new expenses in this category. Expenses that already exist won't change.
Preview matches is hidden, not locked, since there is no set of existing expenses to preview.
Table row and the Categories subsection
Category rules render in a new Categories subsection of the Expense defaults table, positioned after Merchant types. The full subsection order is Categories, Merchants, Merchant types.
- Type: the
Update pill, consistent with every other row in this tab.
- Condition:
Category is "Meals and Entertainment".
- Rule:
Update tax to "GST (10%)", using the tax rate's name as it appears in workspace tax settings.
- Clicking a row opens the same
Apply expense defaults RHP it was created in, with the condition and tax rate pre-filled.
Category rules are admin-created, so unlike Merchant types rows they are editable and deletable.
Subsection-header logic, consistent with R1 and R2: when the only rules in the tab are the locked Merchant types defaults, show a flat list with no headers. Headers appear once at least one admin-created rule exists. A header renders only when its own subsection has at least one row, so a workspace with a category rule and no custom merchant rules sees Merchant types and Categories and no Merchants header.
Truncation: the Rule column truncates with … and shows the full rule text in a tooltip on hover, consistent with existing behavior. A category rule is a single clause, so truncation will be rare here; no change to the tooltip itself though, if it applies.
Empty state: unchanged. The Merchant types defaults always exist, so the tab is never empty and this issue adds no empty state.
Existing Classic-configured defaults
A workspace that already set a category default tax rate in Classic sees a matching row in Categories when this ships. Those rules are the same objects, not copies. Editing the tax rate from this RHP updates the rule Classic reads, and deleting the row removes it in both places.
Mobile
The RHP renders as a full-screen page with the same rows, the same order, and the same locks. The explainer uses the existing modal pattern. No mobile-specific layout.
Category details RHP
No changes to the Category rules section in the category details RHP (#92742). Category tax defaults surface there through that release's existing pattern.
QA Examples
- An admin opens
New rule > Apply expense defaults and sets Category to Meals and Entertainment: the Merchant condition row locks, and in the defaults section every row except Tax locks.
- They set
Tax to GST (10%) and save: a row appears in Categories with the Update pill, Category is "Meals and Entertainment", and Update tax to "GST (10%)".
- A different admin sets
Merchant to Starbucks first: the Category row locks and every row in the defaults section stays enabled, exactly as today.
- In that state they select the locked
Category row: the explainer opens reading This rule already matches on a merchant. Reset the rule to match on a category instead. Nothing changes. They select Reset, both conditions clear, and every row unlocks.
- An admin with a saved category rule selects the locked
Apply to existing unsubmitted expenses toggle: the explainer opens and the toggle stays off. An unsubmitted Meals and Entertainment expense created yesterday keeps no tax; the next expense created in that category gets GST (10%).
- A workspace that set a default tax rate on
Meals and Entertainment in Classic opens Expense defaults after this ships: the rule is already in Categories. Changing its tax rate to VAT (20%) and saving updates the rule Classic reads.
- An admin tries to create a second rule for
Meals and Entertainment: the category picker doesn't offer that category.
- A workspace with only the 12 locked
Merchant types rows adds one category rule: the Merchant types and Categories headers appear, and no Merchants header renders.
- A workspace with taxes turned off opens the flow: the
Category condition row is locked and reads Turn on taxes in your workspace settings to use them. The Merchant condition still works normally.
Issue Owner
Current Issue Owner: @Krishna2323
Background
R1 (#92739) centralized workspace merchant rules into the
Expense defaultstab, and R2 (#92741) added the 12 lockedMerchant typesdefaults. Every rule in that tab matches on a merchant or a merchant type.Classic lets an admin set a default tax rate on a category, and we have no equivalent on the new platform. Any workspace using that setting today loses it when they move over, which makes this a backward-compatibility gap and a blocker on removing the beta.
This issue adds
Categoryas a second condition forApply expense defaults, so an admin can set a default tax rate for a category.Expense defaultshas not yet been migrated to the new rules system, so the rule carries real constraints: one condition per rule, tax as the only available default, and no retroactive application. We express all three with the lock affordance R1 and R2 already established.Scope
Categoryas a second condition to theApply expense defaultsbranch ofNew rule, alongside the existingMerchantcondition.Expense defaultshas not been migrated to the new rules system.Category, allow onlyTaxin the defaults to apply and lock every other field.Apply to existing unsubmitted expensesfor category rules, since a category tax default only applies to expenses created after the rule is saved.Categoriessubsection of theExpense defaultstable.New ruleoption list or its copy. This extends the existingApply expense defaultsbranch; it does not add a fifth rule type.Product and design details
Applicability
The
Categorycondition is available to workspace admins when taxes are enabled on the workspace. Taxes are what the rule sets, so with taxes off there is nothing to configure. In that case theCategorycondition row renders locked, and theMerchantcondition continues to work as it does today.Condition: one of
MerchantorCategoryThe
If any expense matches:section has two rows,MerchantandCategory, in that order. Each shows its selected value beneath its label, and nothing beneath it when unset.Setting a value in one row locks the other:
Got itaction. It changes no selections.Explainer copy, which differs by direction:
Categorylocked, because a merchant is set. Heading:Only one condition per rule. Body:This rule already matches on a merchant. Reset the rule to match on a category instead.Merchantlocked, because a category is set. Heading:Only one condition per rule. Body:This rule already matches on a category. Reset the rule to match on a merchant instead.Categorylocked, because taxes are disabled. Heading:Turn on taxes first. Body:Category rules set a default tax rate. Turn on taxes in your workspace settings to use them.Resetappears in the RHP header once either condition is set. It clears both conditions and every default, and unlocks every row. It does not appear on a rule with nothing selected.Defaults to apply
The
Then apply the following defaults:section lists the sum of all conditions that apply toMerchantandCategory. Which rows are available depends on the condition:Merchant: every row is enabled. This is today's behavior and it does not change.Category: onlyTaxis enabled. Every other row shows the lock icon and opens the explainer when selected.Explainer copy for a row locked by the category condition. Heading:
Only tax is available for category rules. Body:Category rules can set a default tax rate. To set other defaults, match on a merchant instead.An admin can select multiple categories to set tax defaults in bulk. The category picker excludes any category that already has one.
Apply to existing unsubmitted expensesandPreview matchesFor merchant rules, both controls behave exactly as they do today.
For category rules:
Category rules apply going forward. Body:A default tax rate applies to new expenses in this category. Expenses that already exist won't change.Preview matchesis hidden, not locked, since there is no set of existing expenses to preview.Table row and the
CategoriessubsectionCategory rules render in a new
Categoriessubsection of theExpense defaultstable, positioned afterMerchant types. The full subsection order isCategories,Merchants,Merchant types.Updatepill, consistent with every other row in this tab.Category is "Meals and Entertainment".Update tax to "GST (10%)", using the tax rate's name as it appears in workspace tax settings.Apply expense defaultsRHP it was created in, with the condition and tax rate pre-filled.Category rules are admin-created, so unlike
Merchant typesrows they are editable and deletable.Subsection-header logic, consistent with R1 and R2: when the only rules in the tab are the locked
Merchant typesdefaults, show a flat list with no headers. Headers appear once at least one admin-created rule exists. A header renders only when its own subsection has at least one row, so a workspace with a category rule and no custom merchant rules seesMerchant typesandCategoriesand noMerchantsheader.Truncation: the
Rulecolumn truncates with…and shows the full rule text in a tooltip on hover, consistent with existing behavior. A category rule is a single clause, so truncation will be rare here; no change to the tooltip itself though, if it applies.Empty state: unchanged. The
Merchant typesdefaults always exist, so the tab is never empty and this issue adds no empty state.Existing Classic-configured defaults
A workspace that already set a category default tax rate in Classic sees a matching row in
Categorieswhen this ships. Those rules are the same objects, not copies. Editing the tax rate from this RHP updates the rule Classic reads, and deleting the row removes it in both places.Mobile
The RHP renders as a full-screen page with the same rows, the same order, and the same locks. The explainer uses the existing modal pattern. No mobile-specific layout.
Category details RHP
No changes to the
Category rulessection in the category details RHP (#92742). Category tax defaults surface there through that release's existing pattern.QA Examples
New rule>Apply expense defaultsand setsCategorytoMeals and Entertainment: theMerchantcondition row locks, and in the defaults section every row exceptTaxlocks.TaxtoGST (10%)and save: a row appears inCategorieswith theUpdatepill,Category is "Meals and Entertainment", andUpdate tax to "GST (10%)".MerchanttoStarbucksfirst: theCategoryrow locks and every row in the defaults section stays enabled, exactly as today.Categoryrow: the explainer opens readingThis rule already matches on a merchant. Reset the rule to match on a category instead.Nothing changes. They selectReset, both conditions clear, and every row unlocks.Apply to existing unsubmitted expensestoggle: the explainer opens and the toggle stays off. An unsubmittedMeals and Entertainmentexpense created yesterday keeps no tax; the next expense created in that category getsGST (10%).Meals and Entertainmentin Classic opensExpense defaultsafter this ships: the rule is already inCategories. Changing its tax rate toVAT (20%)and saving updates the rule Classic reads.Meals and Entertainment: the category picker doesn't offer that category.Merchant typesrows adds one category rule: theMerchant typesandCategoriesheaders appear, and noMerchantsheader renders.Categorycondition row is locked and readsTurn on taxes in your workspace settings to use them.TheMerchantcondition still works normally.Issue Owner
Current Issue Owner: @Krishna2323