Setup
Enterprise Territory Management, one active model: 3 Geography nodes → 16 Region nodes → ~50 Territory → ~19 Sub-Territory. ~ Opportunities, ~ Accounts, 99.96% of which are assigned to a territory.
Opportunity OWD is already Private, but View All is granted on ~ profiles, so everyone effectively sees every region. We're removing it and replacing it with region-scoped access.
Phase one targets our Sales User profile only (~155 users). Sales managers sit on that same profile, so manager visibility has to come from the territory hierarchy rather than a separate profile. Everyone else keeps View All via a bypass permission for now.
Where I've got to
My plan was:
- Stamp a stored field Visibility_Region__c on Opportunity before save.
- Create 16 criteria-based sharing rules — Visibility_Region__c = 'X' → share with Territory X and Subordinates.
- Check UserTerritory2Association before letting a sales user create an opportunity out of region.
Then I noticed our territories already have OpportunityAccessLevel set, and accounts are almost fully assigned — so native ETM territory sharing would already give us region-scoped access once users are assigned, without any of the above.
The reason I'd built the stamped-field approach is that our region isn't always the account's region: an opportunity can carry an MSP end-user region or a manual override that should win. Native ETM only keys off the account. But that's 1.8% of our opportunities — the other 98% would be handled natively.
My questions
1. Is the stamped field + 16 sharing rules the right call here, or am I overbuilding? Would you lean on native ETM territory access for the 98% and handle the override cases some other way — manual shares, apex sharing, an opportunity team — rather than building a parallel mechanism for all records?
2. How would you handle lead conversion? Conversion creates Opportunities too, and the only hook is Opportunity beforeInsert. addError() there rolls back the entire conversion, destroying the Account and Contact the user just created. What I want is to let the conversion complete but withhold only the Opportunity, then route it to the account owner. I'm considering a screen flow replacing the standard Convert button with createOpportunity = false on the out-of-region branch — is that what people actually do?
3. One factual check: when addError() fires on the Opportunity during convertLead, does the whole transaction roll back cleanly, or can it orphan the Account/Contact?
Any views on territory assignment level — region node versus the specific territory a rep owns — would also be welcome.
#Answers #Salesforce Developer #Territory Management #Object Permissions #Sales Cloud #Salesforce Admin
Hi Rohan,
Native territory access is the right base. Opportunity access on the territory already covers the opportunities for accounts assigned to that territory, and users higher in the hierarchy get that access from the parent territory. Sixteen criteria-based sharing rules would be a second model for the 98 percent ETM already handles, and you'd be keeping both in sync.
For the small set where the opportunity region isn't the account's region, don't stamp a field on every opportunity just to catch those. An opportunity team, a manual share, or a bit of Apex sharing on the override records is enough. Managers can stay on the Sales User profile. Put them on the region node so the hierarchy gives them the child territories, instead of a separate profile with View All.
On conversion, convertLead is one transaction. addError() in an Opportunity before-insert trigger rolls the whole thing back, account and contact included. It doesn't leave an orphan, and it can't withhold only the opportunity.
A screen flow in place of the standard Convert button can do the branch you want. Call the convert action with opportunity creation off on the out-of-region path, let the account and contact land, and then route the opportunity. The standard button can't split the transaction that way.
I'd assign the rep to the territory they own and the manager to the region node above it.