There is one question I would put to any software vendor who says a role "can't see" something.
Is the field removed before it is sent to that person, or is it sent and not shown?
Most vendors will say "not shown" without noticing they have said anything. That answer is worth understanding, because it is the default way software is built, and because it means the restriction is a drawing, not a wall.
Why "not shown" is the default
Think about how a screen gets built. The server loads the order from the database: client, items, prices, totals, history, everything. It hands that whole object to the page. The page decides what to draw. Somewhere late in that process, a developer adds a rule: if the user is from the warehouse, leave out the price column.
The rule works. The warehouse user opens the order and sees no price. But look at the order of events: the price travelled from the database to the user's device first, and was hidden second. The hiding is a display decision, made last, on data that has already arrived.
Nothing about this is lazy. It is how every web framework encourages you to work: fetch the record once, draw it many ways. A role-based rule at the drawing step is the natural place to put it. It is also, for anything that matters, the wrong place.
The test anyone can run
Here is how you tell the two kinds apart without reading a line of code. Log in as the restricted user. Open a record. Press F12, open the Network tab, reload, and look at what the server sent. If the price is in the response and not on the screen, you have a drawing. Anyone with that login and five minutes can read it.
The vendor is not lying when they say the role can't see the price. The screen does not show it. But "can't see" was supposed to mean something stronger.
One number, seven places
To see how far a number travels, follow one.
An order is worth ₹3,40,000. The owner of a rental company does not want the warehouse team to know that. I built the order system for a company with exactly this rule, across six branches. These are the places that number had to come out of, found one at a time.
The order screen. The obvious one, and the one every vendor handles. The warehouse view has no totals and no price columns.
The data behind the order screen. This is the Network-tab case. For a warehouse login the server builds a separate copy of the order with the total, paid amount, balance, tax, discount, advance and line prices removed, and sends only that. The page cannot draw what it never received.
The delivery paper. The challan that travels with the truck is printed from the order's lines, and the lines carry prices. They come out for warehouse logins. One figure stays: the per-piece value typed on the challan, because the warehouse team enter that themselves.
The order's history. "Advance of ₹1,00,000 received" is a line in the order's timeline. The timeline is one list, and the warehouse reads it to see when things were dispatched and returned. So each line is marked, at the moment it is written, as money or not money, and warehouse logins get the second kind only. The marking happens in the one function every timeline entry passes through, so a new kind of entry cannot forget to be marked; the alternative, scanning entries for a rupee sign, breaks the first time someone writes "paid" without one.
Notifications. The alert the warehouse gets for a new order carries the order number, the number of products and the branch. No amount. This is composed separately from the alert the salesperson gets, not copied and trimmed.
The dashboard. A total gives away its parts. If a warehouse login asks for a revenue figure, the request is refused on the server. The number is not fetched and then covered.
Saving. This one is not about reading at all, and it is the one that catches people who have done the other six. Suppose the warehouse edits an order, say the vehicle number on a challan, and the software saves back the copy of the order they were shown. The prices that were blanked for display are now saved as blanks. The redacted copy and the saved copy have to be different objects, and the save has to take only the fields that user is allowed to change.
Seven places, for one number and one role.
The leak the company found
The same question applies to things that are not money.
The sales team in that company shares one client list. When a salesperson picked a client for a quotation, the form filled in the client's phone, email and contact person, to save typing. Helpful, and wrong: a salesperson could look up a colleague's client and leave with the contact. It was worse than the form. The search results carried those fields too, and typing a phone number into the search box found the client it belonged to, which is a reverse lookup nobody designed.
The company flagged it. The fix was the same move as with the order: the copy of a client record that a salesperson receives holds the name, GSTIN, address and pincode, which is what the quotation prints. Search works on name or GSTIN only. The contact fields arrive blank, unless that salesperson has quoted the client before, in which case their own last entry comes back, and nobody else's.
How to prove a field is gone
A fix like this cannot be checked by looking at the screen, because the screen was never the problem.
The way I checked it was to log in as two different salespeople and search everything the server sent back, every response to every click, for the hidden phone number. That is now one of the checks run before a release: a script logs in as each role, drives the screens, and searches every response for the values that must not appear. If the phone number turns up anywhere, the release stops.
That is the only test that answers the vendor question. Not "is it on the screen" but "is it in anything the server sent".
Run it on what you already use
You do not need my system to do this. Log in as your most restricted user. Open a record. Download whatever it lets you download. Read the record's history. Look through last month's notifications. Then search for something that user should not be able to find, and check the Network tab while you do it.
If something turns up, I would like to hear where. I keep finding places I had not counted.